import { cloneDeep } from 'lodash-es';
import { EVENTS, type IEventBus } from '../../events';
import { BATTLE_SIDE, BattleUnit } from './unit';
import { type BattleSkill } from '../battle-skill';
import { BattleManager } from './manager';
import { BaseModel } from '../base';
import { MODEL_NAME } from '../../constants/model-names';
import type { ItemRarity } from '../../data/item/type';
import {
  BATTLE_EVENT_TYPE,
  type BattleEvent,
  type BattleEventLog,
  type BattleEventLogItem,
  type BattleSettings,
} from './type';
import type { BaseSnapshot } from '../../systems/save';

export interface BattleV1 extends BaseSnapshot {
  version: 1;
  settings: BattleSettings;
}

export type BattleSnapshot = BattleV1;

export type BattleSnapshotOld = BattleV1;

export class BattleModel extends BaseModel {
  public name = MODEL_NAME.BATTLE;
  private bus: IEventBus;
  private battleManager: BattleManager;
  private isBattling = false;
  private battleLog: BattleEventLog[] = [];
  /** 战斗设置 */
  private settings: BattleSettings = {
    rarity: [],
  };

  constructor(bus: IEventBus) {
    super();
    this.bus = bus;
    this.battleManager = new BattleManager(20);

    this.battleManager.onEvent(BATTLE_EVENT_TYPE.BATTLE_END, () => {
      this.isBattling = false;
      this.bus.emit(EVENTS.BATTLE_STATE_UPDATE);
    });

    this.battleManager.onEvent(BATTLE_EVENT_TYPE.LOG_UPDATE, (event) => {
      this.updateBattleLog(event);
      this.bus.emit(EVENTS.BATTLE_STATE_UPDATE);
    });
  }

  public addUnit(unit: BattleUnit) {
    this.battleManager.addUnit(unit);
  }

  /** 单步执行回合战斗 */
  public executeSingleAction() {
    this.battleManager.executeSingleAction();
  }

  /** 开始战斗 */
  public startBattle() {
    if (this.isBattling) {
      return;
    }
    this.battleManager.startBattle();
    this.isBattling = true;
    this.bus.emit(EVENTS.BATTLE_STATE_UPDATE);
  }

  /** 停止战斗 */
  public stopBattle() {
    if (!this.isBattling) {
      return;
    }
    this.isBattling = false;
    this.battleManager.reset();
    this.bus.emit(EVENTS.BATTLE_STATE_UPDATE);
  }

  public reset() {
    this.battleManager.reset();
  }

  public getIsBattling() {
    return this.isBattling;
  }

  public setSettingsRarity(rarity: ItemRarity[]) {
    this.settings.rarity = rarity;
    this.bus.emit(EVENTS.BATTLE_STATE_UPDATE);
  }

  public getSettingsRarity() {
    return this.settings.rarity;
  }

  public addbattleLog(
    message: string,
    options?: {
      skill?: BattleSkill;
      items?: BattleEventLogItem[];
    }
  ) {
    this.battleLog.push({
      type: 'MESSAGE',
      message,
      ...options,
    });
  }

  public get uiState() {
    return {
      isBattling: this.isBattling,
      battleLog: cloneDeep(this.battleLog),
      battleInfo: this.battleManager.getBattleState(),
      settings: cloneDeep(this.settings),
    };
  }

  /** 获取所有敌人 */
  public getEnemyUnits() {
    return this.battleManager.getEnemyUnits();
  }

  private addBattleStartLog(event: BattleEvent) {
    if (event.round && event.round === 1) {
      this.battleLog.push({
        type: event.type,
        message: '战斗开始',
      });
    }
  }

  private addBattleDamageLog(event: BattleEvent) {
    const source = event.source;
    const target = event.target;
    const damage = event.damage;
    const damageSkill = event.skill;
    if (
      source === undefined ||
      target === undefined ||
      damage === undefined ||
      damageSkill === undefined
    )
      return;
    this.battleLog.push({
      type: event.type,
      message: `[${source.name}] 使用 [${damageSkill.name}] 攻击 [${target
        .map((target) => target.name)
        .join(', ')}] 造成 ${Math.floor(damage)} 点伤害`,
      skill: damageSkill,
    });
  }

  private addBattleHealLog(event: BattleEvent) {
    const source = event.source;
    const target = event.target;
    const heal = event.heal;
    const healSkill = event.skill;
    if (
      source === undefined ||
      target === undefined ||
      heal === undefined ||
      healSkill === undefined
    )
      return;

    this.battleLog.push({
      type: event.type,
      message: `[${source.name}] 使用 ${healSkill.name} 治疗 [${target
        .map((target) => target.name)
        .join(', ')}] 恢复 ${heal} 点生命值`,
      skill: healSkill,
    });
  }

  private addBattleEndLog(event: BattleEvent) {
    let message = '战斗胜利！';
    if (event.winner) {
      message =
        event.winner === BATTLE_SIDE.PLAYER
          ? '牛逼，战斗胜利！'
          : '被吊起来打，战斗失败！';
    }
    this.battleLog.push({
      type: event.type,
      message,
    });
  }

  private updateBattleLog(event: BattleEvent) {
    if (this.battleLog.length >= 50) {
      this.battleLog.shift();
    }

    switch (event.type) {
      case BATTLE_EVENT_TYPE.BATTLE_START:
        this.addBattleStartLog(event);
        break;
      case BATTLE_EVENT_TYPE.DAMAGE:
        this.addBattleDamageLog(event);
        break;
      case BATTLE_EVENT_TYPE.HEAL:
        this.addBattleHealLog(event);
        break;
      case BATTLE_EVENT_TYPE.BATTLE_END:
        this.addBattleEndLog(event);
        let win = false;
        if (event.winner === BATTLE_SIDE.PLAYER) {
          win = true;
        }
        this.bus.emit(EVENTS.BATTLE_END, win);
        break;
      default:
        break;
    }
  }

  public save(): BattleSnapshot {
    return {
      version: 1,
      settings: {
        ...this.settings,
      },
    };
  }

  public load(snapshot: BattleSnapshot) {
    this.settings = snapshot.settings;
  }
}
